﻿using System;
using System.Net;
using System.Text;
using huqiang;
using huqiang.Data;
using System.Collections.Generic;
using huqiang.Communication;
using System.Runtime.InteropServices;
using DataHandle;
using huqiang.UIModel;

namespace DataControll
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    public struct MsgHead
    {
        /// <summary>
        /// 数据类型
        /// </summary>
        public byte DataType;
        /// <summary>
        /// 指令类型
        /// </summary>
        public byte CmdType;
        /// <summary>
        /// 指令
        /// </summary>
        public UInt16 Cmd;
        /// <summary>
        /// 错误码
        /// </summary>
        public Int16 Error;
        /// <summary>
        /// 数据长度
        /// </summary>
        public int DataLen;
        public unsafe static int Size = sizeof(MsgHead);
    }
    public class KcpSocket : KcpUser
    {
        public bool Keep = false;
        public KcpSocket()
        {
            
        }
        public sealed override void HandleMsg(ref UserMsg msg, string str, byte[] dat)
        {
            if (msg.Error == 0)
            {
                DataProc.HandleMsg(this, msg.DataType, msg.CmdType, msg.Cmd, dat);
            }
            else
            {
                DataProc.HandleError(this, msg.DataType, msg.CmdType, msg.Error, null);
            }
        }
        public override void Connect()
        {
            string str = "Connect OK: " + endPoint.Address + ":" + port;
            KcpDataControll.Instance.AddMainMission((o)=> {
                UIPage.CurrentPage.Cmd(MsgCmd.WriteLine, o);
            },str);
        }
        public override bool Disconnect()
        {
            base.Disconnect();
            return !Keep;
        }

        public static byte[] EncodingData(byte dataType, UInt16 cmd, byte[] dat, byte cmdType = 0, Int16 error = 0)
        {
            int c = 0;
            Encoding encoding = Encoding.UTF8;
            if (dat != null)
                c = dat.Length;
            byte[] buf = new byte[c + MsgHead.Size];
            unsafe
            {
                fixed (byte* bp = &buf[0])
                {
                    MsgHead* ip = (MsgHead*)bp;
                    ip->DataType = dataType;
                    ip->CmdType = cmdType;
                    ip->Cmd = cmd;
                    ip->Error = error;
                    ip->DataLen = c;
                }
                Array.Copy(dat, 0, buf, 12, c);
            }
            return buf;
        }
        public static byte[] EncodeString(byte dataType, UInt16 cmd, string str, byte cmdType, Int16 error)
        {
            int c = 0;
            Encoding encoding = Encoding.UTF8;
            if (str != null & str != "")
                c = encoding.GetByteCount(str);
            byte[] buf = new byte[c + MsgHead.Size];
            if (c > 0)
                encoding.GetBytes(str, 0, str.Length, buf, MsgHead.Size);
            unsafe
            {
                fixed (byte* bp = &buf[0])
                {
                    MsgHead* ip = (MsgHead*)bp;
                    ip->DataType = dataType;
                    ip->CmdType = cmdType;
                    ip->Cmd = cmd;
                    ip->Error = error;
                    ip->DataLen = c;
                }
            }
            return buf;
        }
        public void Send(byte dataType, UInt16 cmd, byte[] dat, byte cmdType = 0, Int16 error = 0)
        {
            Post(0, KcpSocket.EncodingData(dataType, cmd, dat, cmdType, error), 0);
        }

        public void SendString(UInt16 cmd, string str, byte cmdType = 0, Int16 error = 0)
        {
            Post(0, EncodeString(EnvelopeType.String, cmd, str, cmdType, error), 0);
        }
        public void SendJson(UInt16 cmd, string str, byte cmdType = 0, Int16 error = 0)
        {
            Post(0, EncodeString(EnvelopeType.Json, cmd, str, cmdType, error), 0);
        }
    }
    public class KcpDataControll
    {
        static KcpDataControll ins;
        public static KcpDataControll Instance { get { if (ins == null) ins = new KcpDataControll(); return ins; } }
        public long ServerTime;
        public bool Connected
        {
            get
            {
                if (MainLink == null)
                    return false;
                return MainLink.Connected;
            }
        }
        public KcpSocket MainLink;
        KcpServer<KcpSocket> server;
        public KcpDataControll OpenServer(int port)
        {
            if (server == null)
            {
                server = new KcpServer<KcpSocket>(port);
                server.Run(1);
                server.OpenHeart();
            }
            else
            {

            }
            return this;
        }
        public KcpSocket Connection(string ip, int port)
        {
            var address = IPAddress.Parse(ip);
            if (server == null)
            {
                server = new KcpServer<KcpSocket>(0);
                server.Run(1);
                server.OpenHeart();
            }
            var remote = new IPEndPoint(address, port);
            var link = server.FindOrCreateLink(remote);
            server.soc.SendTo(KcpListener.Heart, remote);
            return link;
        }

        public void Send(byte dataType, UInt16 cmd, byte[] dat, byte cmdType = 0, Int16 error = 0)
        {
            MainLink.Send(dataType,cmd,dat,cmdType,error);
        }
       
        public void SendString(UInt16 cmd, string str, byte cmdType = 0, Int16 error = 0)
        {
            MainLink.SendString(cmd, str, cmdType, error);
        }
        public void SendJson(UInt16 cmd, string str, byte cmdType = 0, Int16 error = 0)
        {
            MainLink.SendJson(cmd, str, cmdType, error);
        }
        public void Broadcast(byte dataType, UInt16 cmd, string str, byte cmdType = 0)
        {
            server.PostBroadcast(KcpSocket.EncodeString(dataType, cmd, str, cmdType, 0), 16);
        }
        public void Dispose()
        {
            server.CloseHeart();
            server.Dispose();
        }

        class Mission
        {
            public Action<object> action;
            public object data;
        }
        QueueBuffer<Mission> missions = new QueueBuffer<Mission>(1024);
        public void AddMainMission(Action<object> action, object obj)
        {
            Mission m = new Mission();
            m.action = action;
            m.data = obj;
            missions.Enqueue(m);
        }
        public void ExecuteMainMission()
        {
            int c = missions.Count;
            for (int i = 0; i < c; i++)
            {
                var m = missions.Dequeue();
                if (m != null)
                {
                    try
                    {
                        m.action(m.data);
                    }
                    catch
                    {

                    }
                }
            }
        }
    }
}